#!coding:utf-8
# vim: tabstop=4 shiftwidth=4 softtabstop=4
import sys
from tcloudhd.common import utils, exception, config, constant
import logging
import tempfile
import shutil
import contextlib
import os
from eventlet import tpool

LOG = logging.getLogger(__name__)
conf = config.load_settings()
IMAGE_PATH = conf['tcloud.image.path'] + '/images'

_MKFS_COMMAND = {
    'default':'mkfs.ntfs --force --fast --label %(fs_label)s %(target)s',
    'linux':'mkfs.ext3 -L %(fs_label)s -F %(target)s',
    'windows':'mkfs.ntfs --force --fast --label %(fs_label)s %(target)s'
}

"""
    vm utils 
"""

def execute(*args, **kwargs):
    return utils.execute(*args, **kwargs)
  
def chown(path, owner):
    execute('chown', owner, path)
    

def get_disk_size(path):
    out, _ = execute('qemu-img', 'info', path)
    size = [i.split('(')[1].split()[0] for i in out.split('\n')
        if i.strip().find('virtual size') >= 0]
    return int(size[0])

def copy_file(src, dest):
    shutil.copyfile(src, dest)

def backup_file(src):
    execute('mv',src,src+'.bak')

def resize_disk(disk_path,resize_size):
    execute('qemu-img','resize',disk_path,'+'+str(resize_size)+'G')

def delete_file(file):
    if os.path.exists(file):
        try:
            os.remove(file)
        except:
            LOG.exception('error in remove file')
            raise

    
def ensure_fold(base_path):
    if not os.path.exists(base_path):
        os.makedirs(base_path)

@contextlib.contextmanager
def tempdir(**kwargs):
    tmpdir = tempfile.mkdtemp(**kwargs)
    try:
        yield tmpdir
    finally:
        try:
            shutil.rmtree(tmpdir)
        except OSError, e:
            LOG.debug('Could not remove tmpdir: %s' % str(e))
            
"""
    os.umask(mask)

    Set the current numeric umask and return the previous umask.
    umask 是指 007 770 这种权限
"""
def write_to_file(path, contents,umask=None):
    if umask:
        os_xxx = os.umask(umask)
    try:
        with open(path, 'w') as f:
            f.write(contents)
    finally:
        if umask:
            os.umask(os_xxx)

def get_fs_info(path):
    """Get free/used/total space info for a filesystem

    :param path: Any dirent on the filesystem
    :returns: A dict containing:

             :free: How much space is free (in bytes)
             :used: How much space is used (in bytes)
             :total: How big the filesystem is (in bytes)
    """
    hddinfo = os.statvfs(path)
    total = hddinfo.f_frsize * hddinfo.f_blocks
    free = hddinfo.f_frsize * hddinfo.f_bavail
    used = hddinfo.f_frsize * (hddinfo.f_blocks - hddinfo.f_bfree)
    return {'total': total,
            'free': free,
            'used': used}     
    
    
def get_instance_path(instances_path,existed_vms=0):
    if ',' in instances_path:
        path_list = instances_path.split(',')
        num = existed_vms % len(path_list)
        return path_list[num]
    else:
        return instances_path

def copy_image(src, dest, host=None,block_size=16384):
    """Copy a disk image to an existing directory
    """
    if not host:
        copy_file(src,dest)
    else:
        dest = "%s:%s" % (host, dest)
        try:
            execute('rsync', '--sparse', '--compress', '--dry-run', src, dest)
        except exception.ProcessExecutionError:
            execute('scp', src, dest)
        else:
            execute('rsync', '--sparse', '--compress', src, dest)
            
def cache_base_image(mountpoint,image):
    base_path = os.path.join(mountpoint, '_base')
    ensure_fold(base_path)
    base_img = os.path.join(base_path, '%s.base' % image['name'])
    if not os.path.exists(base_img):
        LOG.info("copying image %s." % image['name'])
        tpool.execute(copy_image,os.path.join(mountpoint,"images",image['name']),base_img)
        LOG.info("Complete copying image %s." % image['name'])
    return base_img

def sync_base(image_id,instances_path):

    def sync(path):
        _base_path = os.path.join(path, '_base')
        ensure_fold(_base_path)
        base_img = os.path.join(_base_path, '%s.base' % image_id)
        copy_image(IMAGE_PATH+"/"+image_id,base_img)

    if ',' in instances_path:
        path_list = instances_path.split(',')
        for path in path_list:
            sync(path)
    else:
        sync(instances_path)

def get_image_size(image_id):
    return os.path.getsize(IMAGE_PATH+"/"+image_id)

def image_upload(local_file,image,callback=False):
    LOG.debug("Uploading image to image host")
    ensure_fold(IMAGE_PATH)

    if callback:
        copy_with_prog(local_file,IMAGE_PATH+"/"+image['imageid'])
    else:
        copy_image(local_file,IMAGE_PATH+"/"+image['imageid'])
    LOG.debug("finish upload image to image host")
    return True

def image_update(image_id,base_image):
    copy_image(IMAGE_PATH+"/"+image_id,base_image)


class ProgressBar:
    """From http://code.activestate.com/recipes/168639/"""
    def __init__(self, minValue = 0, maxValue = 10, totalWidth=12):
        self.progBar = "[]" # This holds the progress bar string
        self.min = minValue
        self.max = maxValue
        self.span = maxValue - minValue
        self.width = totalWidth
        self.amount = 0 # When amount == max, we are 100% done
        self.updateAmount(0) # Build progress bar string

    def updateAmount(self, newAmount = 0):
        if newAmount < self.min: newAmount = self.min
        if newAmount > self.max: newAmount = self.max
        self.amount = newAmount

        # Figure out the new percent done, round to an integer
        diffFromMin = float(self.amount - self.min)
        percentDone = (diffFromMin / float(self.span)) * 100.0
        percentDone = round(percentDone)
        percentDone = int(percentDone)

        # Figure out how many hash bars the percentage should be
        allFull = self.width - 2
        numHashes = (percentDone / 100.0) * allFull
        numHashes = int(round(numHashes))

        # build a progress bar with hashes and spaces
        self.progBar = "[" + '#'*numHashes + ' '*(allFull-numHashes) + "]"

        # figure out where to put the percentage, roughly centered
        percentPlace = (len(self.progBar) / 2) - len(str(percentDone))
        percentString = str(percentDone) + "%"

        # slice the percentage into the bar
        self.progBar = (self.progBar[0:percentPlace] + percentString
                        + self.progBar[percentPlace+len(percentString):])

    def __str__(self):
        return str(self.progBar)

def copy_with_prog(src_file, dest_file, overwrite = False, block_size = 16384):
    if not overwrite:
        if os.path.isfile(dest_file):
            raise IOError("File exists, not overwriting")

    # Open src and dest files, get src file size
    src = open(src_file, "rb")
    dest = open(dest_file, "wb")

    src_size = os.stat(src_file).st_size
    # Set progress bar
    prgb = ProgressBar(totalWidth = 79, maxValue = src_size)
    # Start copying file
    cur_block_pos = 0 # a running total of current position
    while True:
        cur_block = src.read(block_size)

        # Update progress bar
        prgb.updateAmount(cur_block_pos)
        cur_block_pos += block_size

        sys.stdout.write(
            '\r%s\r' % str(prgb)
        )
        # If it's the end of file
        if not cur_block:
            # ..write new line to prevent messing up terminal
            sys.stderr.write('\n')
            break
        else:
            # ..if not, write the block and continue
            dest.write(cur_block)
        #end while

    # Close files
    src.close()
    dest.close()

    # Check output file is same size as input one!
    dest_size = os.stat(dest_file).st_size
    if dest_size != src_size:
        raise IOError(
            "New file-size does not match original (src: %s, dest: %s)" % (
                src_size, dest_size)
        )

